﻿-- this file must be encoded by UTF-8 and be run by LuaJIT 2
-- 导入《俺たちに翼はない》的dat文件中字符串的程序 (by dwing)

local ffi = require("ffi")
ffi.cdef[[
int __stdcall MultiByteToWideChar(int cp, int flag, const char* src, int srclen, char* dst, int dstlen);
int __stdcall WideCharToMultiByte(int cp, int flag, const char* src, int srclen, char* dst, int dstlen, const char* defchar, int* used);
]]
local C = ffi.C

local byte = string.byte
local char = string.char
local floor = math.floor
local format = string.format

local fsrc, fdst
local fsrcp = 0

local function Exit()
	if fdst then fdst:close() end
	if fsrc then fsrc:close() end
	os.exit(0)
end

local function Assert(x, ...)
	if x then return x, ... end
	print("ERROR: " .. ...)
	Exit()
end

local function GetShort(s, i)
	i = i or 1
	local a, b = s:byte(i, i+1)
	a, b = a or 0, b or 0
	return a + b * 0x100
end

local function GetBin(x)
	return char(x % 256, floor(x / 0x100) % 256)
end

local function MB_WC(src, cp)
	local srclen = #src
	local dstlen = srclen * 2
	local dst = ffi.new("char[?]", dstlen)
	dstlen = C.MultiByteToWideChar(cp or 0, 0, src, srclen, dst, dstlen) * 2
	return ffi.string(dst, dstlen)
end

local function WC_MB(src, cp)
	local srclen = floor(#src / 2)
	local dstlen = srclen * 3
	local dst = ffi.new("char[?]", dstlen)
	dstlen = C.WideCharToMultiByte(cp or 0, 0, src, srclen, dst, dstlen, nil, nil)
	return ffi.string(dst, dstlen)
end

local function UTF8StrToGBK(s)
	return WC_MB(MB_WC(s, 65001), 936)
end

local function DumpBin(s)
	for i = 1, #s do
		io.stdout:write(format("%02X ", s:byte(i)))
		if i % 16 == 0 then print() end
	end
	print()
end

local function ChangeStr(s)
	return s:gsub("\\n", "\n"):gsub("\\r", "\r"):gsub("\\\\", "\\")
end

local function Main()
	if not arg[3] then
		print("usage: lua moddat.lua <input-filename.src> <input-filename.txt> <output-filename.dat>")
		Exit()
	end

	io.stdout:write(arg[1] .. " + " .. arg[2] .. " => " .. arg[3] .. " ... ")

	fsrc = Assert(io.open(arg[2], "r"))
	local src = Assert(fsrc:read("*a"))
	fsrc:close()
	fsrc = nil

	local txt, idx = {}, {}
	local err
	for a, b, c, d in src:gmatch("○(%w+)○([^\n]*).-●(%w+)●([^\n]*)") do
		if a ~= c then
			print("ERROR: unmatched tag: " .. a .. " and " .. c)
			Exit()
		else--if b ~= d then
			a = tonumber("0x" .. c)
			if not a then
				print("ERROR: not hex number tag: " .. c)
				Exit()
			end
			if txt[a] then
				print("ERROR: duplicated tag: " .. c)
				Exit()
			end
			txt[a] = { src = UTF8StrToGBK(ChangeStr(b)), dst = UTF8StrToGBK(ChangeStr(d)) }
			if #txt[a].src < #txt[a].dst then
				print("ERROR: the content is too long at tag: " .. c)
				err = true
			else
				idx[#idx + 1] = a
			end
		end
	end
	if err then Exit() end
	table.sort(idx)
	io.stdout:write("[" .. #idx .. "] ... ")

	fsrc = Assert(io.open(arg[1], "rb"))
	local src = Assert(fsrc:read("*a"))
	fsrc:close()
	fsrc = nil

	fdst = Assert(io.open(arg[3], "wb"))

	local p = 1
	for _, v in ipairs(idx) do
		if v < p - 1 then
			print(format("ERROR: overlapping string at %04X", v))
			Exit()
		end
		Assert(fdst:write(src:sub(p, v))); p = v + 1
		Assert(fdst:write(txt[v].dst))
		Assert(fdst:write(string.rep("\0", #txt[v].src - #txt[v].dst))); p = p + #txt[v].src
	end
	Assert(fdst:write(src:sub(p)))

	print("OK!")
	Exit()
end

Main()
